#include "Config.h"
#include "include/PowerLibraryEngine.h"

#include "Win32Device.h"
#include "Log.h"
#include "TimerManager.h"

#ifdef __COMPILE_WITH_OPENGL__
    #include "Drivers/OpenGL/OpenGLDriverWin32.h"
    #include "Drivers/OpenGL/OpenGLDriver.h"
    #include "Drivers/OpenGL/OpenGLTextureManager.h"
#endif

#include "scene/SceneManager.h"
#include "InputManager.h"
#include "FileSystem.h"
#include "GUI/GUI.h"
#include "StateManager.h"
#include "ConfigManager.h"
#include "RandomGenerator.h"

static const s8* ClassName = "CWin32Device";

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			BeginPaint(hWnd, &ps);
			EndPaint(hWnd, &ps);
		}
		return 0;

	case WM_ERASEBKGND:
		return 0;

	case WM_MOUSEMOVE:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
		{
			SMouseState newMouseState;
			newMouseState.Pos.X = (short)LOWORD(lParam);
			newMouseState.Pos.Y = (short)HIWORD(lParam);
			newMouseState.LeftButtonPressed = ((wParam&MK_LBUTTON)?true:false);
			newMouseState.MidleButtonPressed = ((wParam&MK_MBUTTON)?true:false);
			newMouseState.RightButtonPressed = ((wParam&MK_RBUTTON)?true:false);

			vector2ds16 PosDif(newMouseState.Pos);
			PosDif -= InputManager->GetMouseState()->Pos;
			newMouseState.PosDif = PosDif;
			InputManager->SetMouseState(&newMouseState);
//			pLogDebug("Pos: (%u,%u), PosDif: (%d,%d) Buttons: L:%u M:%u R:%u\n",newMouseState.Pos.X,newMouseState.Pos.Y,
//				newMouseState.PosDif.X,newMouseState.PosDif.Y,
//				newMouseState.LeftButtonPressed,
//				newMouseState.MidleButtonPressed,newMouseState.RightButtonPressed);
			return 0;
		}
		break;

	case WM_SYSKEYDOWN:
	case WM_SYSKEYUP:
	case WM_KEYDOWN:
	case WM_KEYUP:
	{
		BYTE allKeys[256];

		SKeyInput KeyInput;
		KeyInput.Key = (EKEY_CODE)wParam;
		KeyInput.PressedDown = (message==WM_KEYDOWN || message == WM_SYSKEYDOWN);

		WORD KeyAsc=0;
		GetKeyboardState(allKeys);
		ToAscii((UINT)wParam,(UINT)lParam,allKeys,&KeyAsc,0);

		if (KeyInput.Key==KEY_SHIFT)
		{
			if ((allKeys[VK_LSHIFT] & 0x80)!=0)
				KeyInput.Key=KEY_LSHIFT;
			else if ((allKeys[VK_RSHIFT] & 0x80)!=0)
				KeyInput.Key=KEY_RSHIFT;
		}

		KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0);
		KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0);
		KeyInput.Char = (KeyAsc & 0x00ff); //KeyAsc >= 0 ? KeyAsc : 0;

		InputManager->NewKey(&KeyInput);

		if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
			return DefWindowProc(hWnd, message, wParam, lParam);
		else
			return 0;
	}
	break;

	case WM_SIZE:
	{
		// resize
/*		dev = getDeviceFromHWnd(hWnd);
		if (dev)
			dev->OnResized();*/
	}
	return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_SYSCOMMAND:
		// prevent screensaver or monitor powersave mode from starting
		if ((wParam & 0xFFF0) == SC_SCREENSAVE ||
			(wParam & 0xFFF0) == SC_MONITORPOWER)
			return 0;
		break;

	case WM_USER:
/*		event.EventType = irr::EET_USER_EVENT;
		event.UserEvent.UserData1 = (irr::s32)wParam;
		event.UserEvent.UserData2 = (irr::s32)lParam;
		dev = getDeviceFromHWnd(hWnd);

		if (dev)
			dev->postEventFromUser(event);
*/
		return 0;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

bool CWin32Device::CreatePLEWindow()
{
		// calculate client size
		RECT clientSize;
		clientSize.top = 0;
		clientSize.left = 0;
		clientSize.right = drvp->LenX;
		clientSize.bottom = drvp->LenY;

		DWORD style = WS_POPUP;

		if (!drvp->FullScreen)
			style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

		AdjustWindowRect(&clientSize, style, FALSE);

		const s32 realWidth = clientSize.right - clientSize.left;
		const s32 realHeight = clientSize.bottom - clientSize.top;

		s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
		s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

		if (drvp->FullScreen)
		{
			windowLeft = 0;
			windowTop = 0;
		}

		HINSTANCE hInstance = GetModuleHandle(0);

		HWnd = CreateWindow(ClassName,drvp->TitleDefault.c_str(),style,windowLeft,windowTop,realWidth,realHeight, NULL, NULL,hInstance, NULL);
		drvp->Window = HWnd;
		ShowWindow(HWnd, SW_SHOW);
		UpdateWindow(HWnd);

		MoveWindow(HWnd, windowLeft, windowTop, realWidth, realHeight, TRUE);

		SetActiveWindow(HWnd);
		SetForegroundWindow(HWnd);

		return true;
}

CWin32Device::CWin32Device(StartDriverParams *DrvParams):CCommonDevice(DrvParams)
{
	SetName("Win32Device");

	drvp = DrvParams;

    CreateHelpClasses();
/*
	// get windows version and create OS operator
	core::stringc winversion;
	getWindowsVersion(winversion);
	Operator = new COSOperator(winversion.c_str());
	os::Printer::log(winversion.c_str(), ELL_INFORMATION);
*/
	if (drvp->drvType != DRV_NULLDRIVER)
	{
		// get handle to exe file
		HINSTANCE hInstance = GetModuleHandle(0);

		// Register Class
		WNDCLASSEX wcex;
		wcex.cbSize		= sizeof(WNDCLASSEX);
		wcex.style		= CS_HREDRAW | CS_VREDRAW;
		wcex.lpfnWndProc	= WndProc;
		wcex.cbClsExtra		= 0;
		wcex.cbWndExtra		= 0;
		wcex.hInstance		= hInstance;
		wcex.hIcon		= NULL;
		wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
		wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
		wcex.lpszMenuName	= 0;
		wcex.lpszClassName	= ClassName;
		wcex.hIconSm		= 0;

		// if there is an icon, load it
		//	wcex.hIcon = (HICON)LoadImage(hInstance, "irrlicht.ico", IMAGE_ICON, 0,0, LR_LOADFROMFILE);

		RegisterClassEx(&wcex);
	}
}

CWin32Device::~CWin32Device()
{
}

void CWin32Device::CreateOpenGLDriver()
{
    COpenGLDriverWin32 *Temp = new COpenGLDriverWin32(drvp);
    Temp->EnableOpenGL((HWND)drvp->Window,drvp);
    SetVideoDriver(Temp);
    SetTextureManager(new COpenGLTextureManager());
}

stringc CWin32Device::GetCaption()
{
	s8 *TempText;
	u32 Size = GetWindowTextLength(HWnd);

	if (Size)
	{
		TempText = new s8[Size+1];
		GetWindowText(HWnd,TempText,Size);
		stringc Ret(TempText);
		DEALLOCATEARRAY(TempText);
		return Ret;
	}
	return stringc("");
}

void CWin32Device::SetCaption(stringc Title)
{
	SetWindowText(HWnd,Title.c_str());
}

bool CWin32Device::Update()
{
	CCommonDevice::PreUpdate();

	MSG msg;
	bool quit = false;

	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);

		WndProc(HWnd, msg.message, msg.wParam, msg.lParam);

		if (msg.message == WM_QUIT)
			quit = true;
	}

	bool qcommon = CCommonDevice::PostUpdate();

	return !quit || qcommon;
}
